【Java多线程学习4】volatile关键字及其作用 您所在的位置:网站首页 java volatile指令重排 【Java多线程学习4】volatile关键字及其作用

【Java多线程学习4】volatile关键字及其作用

2024-01-21 13:08| 来源: 网络整理| 查看: 265

说说对于volatile关键字的理解,及的作用 概述

1、我们知道要想线程安全,就需要保证三大特性:原子性,有序性,可见性。

2、被volatile关键字修饰的变量,可以保证其可见性和有序性,但是volatile关键字无法保证对变量操作的原子性。

可见性:使用volatile修饰变量,就是告诉JVM,这个变量是共享且不稳定的,每次使用它都需要到主存中进行读取。有序性:保证有序性这块主要是指被volatile修饰的关键字,其可以有效的防止变量的指令重排序(通过插入内存屏障的方式来实现防止指令重排序)。 一、volatile如何保证变量的可见性?

被volatile修饰的变量,线程每次读取这样的变量都会从主内存获取最新的值,用于保证自己可以看见其他线程对于该变量的修改并获取到最新的值;而线程每次修改这样的变量也都会同步回写到主内存中,用来保证其他线程访问该变量时可以看到自己对于变量的修改并获取到最新的值。 在这里插入图片描述 在这里插入图片描述

二、volatile关键字如何有效防止指令重排序

在Java中,volatile关键字除了可以保证变量的可见性,还有一个重要的作用就是防止JVM的指令重排序。如果我们将变量声明为volatile修饰的变量,在对这个变量进行读写操作的时候,就会插入特定的内存屏障 (lock前缀指令) 的方式来禁止指令重排序。

最典型的例子就是使用双重校验加锁方式创建单例模式,具体代码如下:

public class SingleObject{ //volatile关键字防止指令重排序造成的空指针异常(通过插入特定的内存屏障的方式来禁止指令重排序) private static volatile SingleObject object; //私有构造方法 private SingleObject() {} public static SingleObject getSingleObject() { //第一次检查防止每次获取bean都加锁,减小锁的锁的粒度,提升性能 if (object == null) { //加锁,防止第一次创建实例化时,并发线程多次创建对象 synchronized (SingleObject.class) { //第二次检查判断对象没有实例化,则进行对象的实例化 if (object == null) { object = new SingleObject(); } } } return object; } }

问题一:object为什么要采用volatile关键字修饰? object = new SingleObject();这段代码其实分为三步执行:

1、为object分配内存空间。2、初始化object对象。3、将object指向分配的内存地址。

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getSingleObject()后发现 object不为空,因此返回 object,但此时 object还未被初始化。

问题二:volatile修饰的变量如何有效防止指令重排序? volatile关键字通过插入lock前缀形式的内存屏障方式来有效的防止指令重排序。即在分配内存和赋值操作后插入lock前缀指令(内存屏障),等这两步(上述1、2步)执行完成后,再执行3返回object,就可以实现防止指令重排序。

指令重拍导致的问题就是在内存中分配内存并赋值之前将object返回导致空指针异常,现在在这两步中间加了一层lock前缀指令(内存屏障),保证返回singleton之前分配内存赋值等操作执行完就可以防止指令重拍造成的问题了。 在这里插入图片描述

三、volatile关键字能保证原子性吗?

结论:volatile关键字能保证被修饰变量的可见性,但不能保证对变量操作的原子性。

我们直接上代码演示一个例子:

public class practice3 { //声明一个被volatile修饰的int型变量 private static volatile int x = 0; //x自增函数 public static void inc() { x++; } public static void main(String[] args) throws InterruptedException { for (int i = 0; i for (int j = 0; j //声明一个被volatile修饰的int型变量 private static volatile int x = 0; //通过synchronzied来修饰x自增函数(****) public synchronized static void inc() { x++; } public static void main(String[] args) throws InterruptedException { for (int i = 0; i for (int j = 0; j //声明一个被volatile修饰的int型变量 private static volatile int x = 0; static Lock lock = new ReentrantLock(); //使用ReentrantLock独占锁锁住x自增操作 public static void inc() { lock.lock(); try { x++; } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { for (int i = 0; i for (int j = 0; j //声明一个被volatile修饰的int型变量 private AtomicInteger x = new AtomicInteger(); //x自增函数 public static void inc() { //获取当前值并+1 x.getAndIncrement(); } public static void main(String[] args) throws InterruptedException { for (int i = 0; i for (int j = 0; j


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有